﻿// -------------------------------------------------------------------------
//    @FileName         :    NFWorldPlayer.cpp
//    @Author           :    gaoyi
//    @Date             :   2023-07-10
//    @Email			:    445267987@qq.com
//    @Module           :    NFWorldPlayer.cpp
//
// -------------------------------------------------------------------------

#include "NFPlayer.h"

#include <NFEventDisp.h>

#include "NFLogicCommon/NFLogicShmTypeDefines.h"
#include "NFLogicCommon/NFLogicCommon.h"
#include "NFServerComm/NFServerCommon/NFIServerMessageModule.h"
#include "NFComm/NFCore/NFRandom.hpp"
#include "NFComm/NFCore/NFTimeUtility.h"
#include <NFPacketSendUtil.h>
#include <NFPlayerUtil.h>
#include "NFPlayerMgr.h"
#include "NFPart.h"
#include "NFOnlineService.h"
#include "NFGameCommon/NFMath.h"
#include "NFSession.h"
#include "NFTransSaveRole.h"
#include "ZoneMergeDescEx.h"
#include "NFComm/NFCore/NFServerTime.h"

#define PLAYER_ONLINESYN_TIME_GAP    20

NFPlayer::NFPlayer()
{
    if (EN_OBJ_MODE_INIT == NFShmMgr::Instance()->GetCreateMode())
    {
        CreateInit();
    }
    else
    {
        ResumeInit();
    }
}

NFPlayer::~NFPlayer()
{
}

int NFPlayer::CreateInit()
{
    ///////////////////////////
    m_pPart.resize(PART_MAX);
    m_iClientSessionID = 0;
    m_pClientSession = nullptr;
    /////////////////////////
    m_bIsSyncedRoleDetail = false;
    m_bEventLogFlag = false;
    m_bIsCreateLogin = false;
    m_iPlayerAccountId = INVALID_ID;
    m_CSMsgBinCache.Initialize();
    m_lastOnlineSyncTime = 0;
    m_lastOnlineSyncTime = NF_ADJUST_TIMENOW();
    m_loginZoneId = 0;
    return 0;
}

int NFPlayer::ResumeInit()
{
    return 0;
}

int NFPlayer::OnTimer(int timeId, int callCount)
{
    return 0;
}

int NFPlayer::OnExecute(uint32_t serverType, uint32_t nEventID, uint32_t bySrcType, uint64_t nSrcID, const google::protobuf::Message* pMessage)
{
    /*switch (nEventID)
    {
        default:
            break;
    }*/
    return 0;
}

bool NFPlayer::IsCanLogout()
{
    if (m_iTransNum > 0 || FindModule<NFICoroutineModule>()->IsExistUserCo(m_ullRoleId))
    {
        if ((uint64_t)NF_ADJUST_TIMENOW() - GetCacheCreateTime() < PLAYER_MAX_DISCONNECT_RECONNECT_TIME * m_timeMulti * 3)
        {
            return false;
        }
    }

    if (IsInBattle() || IsInTransSceneing())
    {
        if ((uint64_t)NF_ADJUST_TIMENOW() - GetCacheCreateTime() < PLAYER_MAX_DISCONNECT_RECONNECT_TIME * m_timeMulti * 3)
        {
            return false;
        }
    }
    return true;
}

int NFPlayer::DoOnline()
{
    LogicTick();
    return 0;
}

bool NFPlayer::IsCanDelete()
{
    if (FindModule<NFICoroutineModule>()->IsExistUserCo(m_ullRoleId))
    {
        return false;
    }

    if (!IsNeedSave())
    {
        if (m_iTransNum <= 0)
        {
            return true;
        }
    }
    else if (IsSavingDb())
    {
        return false;
    }
    else
    {
        if ((m_lastSaveDbTime + LOGIC_SERVER_SAVE_PLAYER_TO_DB_TIME < (uint64_t)NF_ADJUST_TIMENOW() && m_lastSaveDbTime + 86400 > (uint64_t)NF_ADJUST_TIMENOW()) || NFGlobalSystem::Instance()->IsServerStopping())
        {
            SendTransToDb(TRANS_SAVEROLEDETAIL_LOGOUT);
        }
    }
    return false;
}

int NFPlayer::DoLogout()
{
    return NFPlayerUtil::LogoutPlayer(this);
}

int NFPlayer::Init()
{
    SetPlayerStatus(CACHE_STATUS_NONE);
    std::vector<NFPart*> vec;
    for (int i = 0; i < (int)m_pPart.size(); ++i)
    {
        m_pPart[i] = CreatePart(i);
        if (nullptr == m_pPart[i])
        {
            NFLogError(NF_LOG_DEFAULT, m_ullRoleId, "Player Init, Create Part Failed, playerId:{} part:{}", m_ullRoleId, i);
            for (int i = 0; i < (int)vec.size(); i++)
            {
                FindModule<NFIMemMngModule>()->DestroyObj(vec[i]);
            }
            return -1;
        }
        vec.push_back(m_pPart[i].GetPoint());
    }

    return 0;
}

int NFPlayer::UnInit()
{
    for (int i = 0; i < (int)m_pPart.size(); ++i)
    {
        if (m_pPart[i])
        {
            RecylePart(m_pPart[i]);
        }
    }

    return 0;
}

int NFPlayer::LoadFromDB(const proto_ff::tbRoleDetail* pDbData, const RoleOne& stRoleOne)
{
    NFPlayerBase::LoadFromDB(pDbData, stRoleOne);
    m_loginZoneId = ZoneMergeDescEx::Instance()->GetMergeZoneId(m_stRoleBase.world_id);
    for (int i = 0; i < (int)m_pPart.size(); ++i)
    {
        if (m_pPart[i])
        {
            m_pPart[i]->LoadFromDB(pDbData);
        }
    }
    m_bIsSyncedRoleDetail = true;
    return 0;
}

int NFPlayer::InitConfig(proto_ff::tbRoleDetail* pDbData, const RoleOne& stRoleOne)
{
    NFPlayerBase::InitConfig(pDbData, stRoleOne);
    for (int i = 0; i < (int)m_pPart.size(); ++i)
    {
        if (m_pPart[i])
        {
            m_pPart[i]->InitConfig(pDbData, stRoleOne);
        }
    }
    return 0;
}

int NFPlayer::OnRegister()
{
    for (int i = 0; i < (int)m_pPart.size(); ++i)
    {
        if (m_pPart[i])
        {
            m_pPart[i]->OnRegister();
        }
    }
    return 0;
}

int NFPlayer::OnLogin()
{
    SetPlayerStatus(CACHE_STATUS_ONLINE);
    SetLastLoginTime(NF_ADJUST_TIMENOW());
    MarkDirty();

    for (int i = 0; i < (int)m_pPart.size(); ++i)
    {
        if (m_pPart[i])
        {
            m_pPart[i]->OnLogin();
        }
    }
    return 0;
}

int NFPlayer::OnLogout()
{
    SetLastLogoutTime(NF_ADJUST_TIMENOW());
    for (int i = 0; i < (int)m_pPart.size(); ++i)
    {
        if (m_pPart[i])
        {
            m_pPart[i]->OnLogout();
        }
    }

    return 0;
}

int NFPlayer::OnDisconnect()
{
    m_lastDisconnectTime = NF_ADJUST_TIMENOW();
    for (int i = 0; i < (int)m_pPart.size(); ++i)
    {
        if (m_pPart[i])
        {
            m_pPart[i]->OnDisconnect();
        }
    }

    return 0;
}

int NFPlayer::OnReconnect()
{
    for (int i = 0; i < (int)m_pPart.size(); ++i)
    {
        if (m_pPart[i])
        {
            m_pPart[i]->OnReconnect();
        }
    }
    return 0;
}

int NFPlayer::LogicTick()
{
    NFAutoLogContext autoLogContext(GetOpenID(), GetRoleID(), 0, 0, 0);

    for (int i = 0; i < (int)m_pPart.size(); ++i)
    {
        if (m_pPart[i])
        {
            m_pPart[i]->LogicTick();
        }
    }

    SaveToDb(60, TRANS_SAVEROLEDETAIL_NORMAL, false);

    SendSyncData();
    return 0;
}

int NFPlayer::SaveDB(proto_ff::tbRoleDetail* pDbData)
{
    NFPlayerBase::SaveDB(pDbData);
    for (int i = 0; i < (int)m_pPart.size(); ++i)
    {
        if (m_pPart[i])
        {
            m_pPart[i]->SaveDB(pDbData);
        }
    }
    return 0;
}

int NFPlayer::SaveRoleSimpleInfo(proto_ff::Proto_RoleSimpleInfo* pDbData)
{
    NFPlayerBase::SaveRoleSimpleInfo(pDbData);
    for (int i = 0; i < (int)m_pPart.size(); ++i)
    {
        if (m_pPart[i])
        {
            m_pPart[i]->SaveRoleSimpleInfo(pDbData);
        }
    }
    return 0;
}

int NFPlayer::DayTick()
{
    if (m_stRoleBase.last_day_zero_update_time == 0)
    {
        m_stRoleBase.last_day_zero_update_time = NF_ADJUST_TIMENOW();
    }

    if (NFTimeUtility::IsSameDayStd(NF_ADJUST_TIMENOW(), m_stRoleBase.last_day_zero_update_time, 0))
    {
        return 0;
    }

    m_stRoleBase.last_day_zero_update_time = NF_ADJUST_TIMENOW();
    MarkDirty();

    for (int i = 0; i < (int)m_pPart.size(); ++i)
    {
        if (m_pPart[i])
        {
            m_pPart[i]->DayTick();
        }
    }
    return 0;
}

int NFPlayer::DayOtherTick()
{
    if (m_stRoleBase.last_day_other_update_time == 0)
    {
        m_stRoleBase.last_day_other_update_time = NF_ADJUST_TIMENOW();
    }

    if (NFTimeUtility::IsSameDay(NF_ADJUST_TIMENOW(), m_stRoleBase.last_day_zero_update_time))
    {
        return 0;
    }

    m_stRoleBase.last_day_other_update_time = NF_ADJUST_TIMENOW();
    MarkDirty();

    for (int i = 0; i < (int)m_pPart.size(); ++i)
    {
        if (m_pPart[i])
        {
            m_pPart[i]->DayOtherTick();
        }
    }
    return 0;
}

int NFPlayer::WeekTick()
{
    if (m_stRoleBase.last_week_update_time == 0)
    {
        m_stRoleBase.last_week_update_time = NF_ADJUST_TIMENOW();
    }

    if (NFTimeUtility::IsSameWeekStd(NF_ADJUST_TIMENOW(), m_stRoleBase.last_week_update_time))
    {
        return 0;
    }

    m_stRoleBase.last_week_update_time = NF_ADJUST_TIMENOW();
    MarkDirty();

    for (int i = 0; i < (int)m_pPart.size(); ++i)
    {
        if (m_pPart[i])
        {
            m_pPart[i]->WeekTick();
        }
    }
    return 0;
}

int NFPlayer::MonthTick()
{
    if (m_stRoleBase.last_month_update_time == 0)
    {
        m_stRoleBase.last_month_update_time = NF_ADJUST_TIMENOW();
    }

    if (NFTimeUtility::IsSameMonthStd(NF_ADJUST_TIMENOW(), m_stRoleBase.last_month_update_time))
    {
        return 0;
    }

    m_stRoleBase.last_month_update_time = NF_ADJUST_TIMENOW();
    MarkDirty();

    for (int i = 0; i < (int)m_pPart.size(); ++i)
    {
        if (m_pPart[i])
        {
            m_pPart[i]->MonthTick();
        }
    }
    return 0;
}

int NFPlayer::SecTick()
{
    for (int i = 0; i < (int)m_pPart.size(); ++i)
    {
        if (m_pPart[i])
        {
            m_pPart[i]->SecTick();
        }
    }
    return 0;
}

int NFPlayer::FiveSecTick()
{
    for (int i = 0; i < (int)m_pPart.size(); ++i)
    {
        if (m_pPart[i])
        {
            m_pPart[i]->FiveSecTick();
        }
    }

    DayTick();
    DayOtherTick();
    WeekTick();
    MonthTick();
    return 0;
}

int NFPlayer::MinuteTick()
{
    for (int i = 0; i < (int)m_pPart.size(); ++i)
    {
        if (m_pPart[i])
        {
            m_pPart[i]->MinuteTick();
        }
    }

    return 0;
}

int NFPlayer::FiveMinuteTick()
{
    for (int i = 0; i < (int)m_pPart.size(); ++i)
    {
        if (m_pPart[i])
        {
            m_pPart[i]->FiveMinuteTick();
        }
    }
    return 0;
}

int NFPlayer::HourTick()
{
    for (int i = 0; i < (int)m_pPart.size(); ++i)
    {
        if (m_pPart[i])
        {
            m_pPart[i]->HourTick();
        }
    }
    return 0;
}

bool NFPlayer::IsNeedSave() const
{
    if (IsUrgentNeedSave())
    {
        return true;
    }

    for (int i = 0; i < (int)m_pPart.size(); ++i)
    {
        if (m_pPart[i] && m_pPart[i]->IsUrgentNeedSave())
        {
            return true;
        }
    }

    return false;
}

int NFPlayer::SendTransToDb(int iReason)
{
    NFTransSaveRole* pTrans = NFTransSaveRole::CreateTrans();
    CHECK_NULL_WF(GetRoleID(), pTrans, "CreateTrans failed, NFTransSaveRole used num:{} all num:{}", NFTransSaveRole::GetStaticUsedCount(), NFTransSaveRole::GetStaticItemCount());
    int iRetCode = pTrans->SaveDb(this, iReason);
    CHECK_ERR_AND_FIN_TRANS(iRetCode, pTrans, "SaveDb failed, iReason:{}", iReason);
    return 0;
}

uint32_t NFPlayer::GetAllSeq() const
{
    uint32_t seq = GetCurSeq();
    for (int i = 0; i < (int)m_pPart.size(); ++i)
    {
        if (m_pPart[i])
        {
            seq += m_pPart[i]->GetCurSeq();
        }
    }
    return seq;
}

void NFPlayer::ClearAllSeq()
{
    ClearUrgent();
    for (int i = 0; i < (int)m_pPart.size(); ++i)
    {
        if (m_pPart[i])
        {
            m_pPart[i]->ClearUrgent();
        }
    }
}

NFPart* NFPlayer::GetPart(uint32_t partType)
{
    if (partType >= PART_NONE && partType < (uint32_t)m_pPart.size())
    {
        return m_pPart[partType].GetPoint();
    }

    return NULL;
}

const NFPart* NFPlayer::GetPart(uint32_t partType) const
{
    if (partType >= PART_NONE && partType < (uint32_t)m_pPart.size())
    {
        return m_pPart[partType].GetPoint();
    }

    return NULL;
}

NFPart* NFPlayer::CreatePart(uint32_t partType)
{
    NFPart* pPart = dynamic_cast<NFPart*>(FindModule<NFIMemMngModule>()->CreateObj(EOT_LOGIC_PART_MIN_ID + partType));
    if (pPart)
    {
        int iRet = pPart->Init(this, partType);
        if (iRet != 0)
        {
            NFLogError(NF_LOG_DEFAULT, 0, "{}::Init Failed", pPart->GetClassName());
            FindModule<NFIMemMngModule>()->DestroyObj(pPart);
            return NULL;
        }
    }
    else
    {
        NFLogError(NF_LOG_DEFAULT, m_ullRoleId, "Create Part Failed, partType Not Handle:{}", partType);
    }
    return pPart;
}

int NFPlayer::RecylePart(NFPart* pPart)
{
    CHECK_NULL(0, pPart);
    pPart->UnInit();
    FindModule<NFIMemMngModule>()->DestroyObj(pPart);
    return 0;
}

bool NFPlayer::IsInBattle()
{
    return false;
}

bool NFPlayer::IsInTransSceneing()
{
    return false;
}

int NFPlayer::BindClientSession(NFSession* pSession)
{
    if (nullptr != pSession)
    {
        m_iClientSessionID = pSession->GetGlobalId();
        m_pClientSession = pSession;
    }
    else
    {
        m_iClientSessionID = 0;
        m_pClientSession = nullptr;
    }

    return 0;
}

int NFPlayer::UnBindClientSession()
{
    m_iClientSessionID = 0;
    m_pClientSession = nullptr;
    return 0;
}

NFSession* NFPlayer::GetClientSession()
{
    m_pClientSession = NFSession::GetObjByGlobalId(m_iClientSessionID);
    return m_pClientSession;
}

NFSession* NFPlayer::GetClientSessionFast()
{
    if (m_pClientSession.GetPoint() == NULL)
    {
        m_pClientSession = NFSession::GetObjByGlobalId(m_iClientSessionID);
    }

    return m_pClientSession;
}

int NFPlayer::CleanupCache()
{
    m_CSMsgBinCache.Clear();
    m_wLastPopedServerSeq = 0;

    return 0;
}

enum
{
    SEQ_SMALL_SCOPE = 1000,
    SEQ_CEIL = USHRT_MAX - SEQ_SMALL_SCOPE,
};

//发包给客户端时调用，缓存回包
int NFPlayer::CacheMsgRes(uint16_t wCmd, uint16_t wSeq, const char* pBin, size_t iSize)
{
    if (wSeq > SEQ_CEIL)
    {
        LOGSVR_FATAL("serverseq:" << wSeq << " is too big, last poped:" << m_wLastPopedServerSeq << ". cmd:" << wCmd);
        return -1;
    }
    //回包小于上一个被弹出的消息序号，说明Session回包序号被重置，此时应该先清空cache
    if (wSeq <= m_wLastPopedServerSeq)
    {
        LOGSVR_FATAL("serverseq " << wSeq << " not bigger than lastpopedserverseq " << m_wLastPopedServerSeq << ", so cleanup cache. cmd:" << wCmd);
        CleanupCache();
    }

    //在黑名单里
    if (IS_CS_RSP_MSG_IN_BLACKLIST(wCmd))
    {
        return 0;
    }

    CHECK_EXPR(iSize < m_CSMsgBinCache.MaxSize(), -1, "unexpected msg cache size:{} more than:{}", iSize, m_CSMsgBinCache.MaxSize());

    while (!m_CSMsgBinCache.IsEmpty() && !m_CSMsgBinCache.CanWrite(iSize))
    {
        m_CSMsgBinCache.Front(m_wLastPopedServerSeq);
        LOGSVR_DEBUG("CacheMsgRes can't write " << iSize << ", cache " << m_CSMsgBinCache.Size() << "/" << m_CSMsgBinCache.MaxSize() << ", pop out seq " << m_wLastPopedServerSeq);
        m_CSMsgBinCache.Pop();
    }

    if (m_CSMsgBinCache.Push(wSeq, pBin, iSize) < 0)
    {
        LOGSVR_ERROR("CachedMsgRes failed:seq " << wSeq << ", size " << iSize);
        return -1;
    }

    LOGSVR_TRACE("CacheMsgRes seq " << wSeq << " size " << iSize << ", buff size " << m_CSMsgBinCache.Size());

    return 0;
}

//发送缓存回包
int NFPlayer::SendCachedMsgRes(uint16_t wSeq)
{
    LOGSVR_DEBUG("SendCachedMsgRes buff size " << m_CSMsgBinCache.Size());

    CHECK_EXPR(wSeq > m_wLastPopedServerSeq, -2, "clientserverseq:{}  smaller than lastpopedserverseq:{}, must relogin", wSeq, m_wLastPopedServerSeq);

    CircularBuffer<uint16_t, MAX_CACHED_MSG_SIZE>::Iterator begin = m_CSMsgBinCache.Begin();
    CircularBuffer<uint16_t, MAX_CACHED_MSG_SIZE>::Iterator end = m_CSMsgBinCache.End();

    NFBuffer buffer;
    buffer.AssureSpace(PROTOCOL_CHAR_BUFF_SIZE);

    for (; begin != end; ++begin)
    {
        uint16_t wTmpSeq = 0;
        begin.Read(wTmpSeq);

        if (wTmpSeq > wSeq)
        {
            buffer.Clear();
            size_t iLen = buffer.WritableSize();
            begin.Read(wTmpSeq, buffer.WriteAddr(), &iLen);

            if (iLen > 0)
            {
                buffer.Produce(iLen);
                NFrame::Proto_FramePkg pkg;
                pkg.ParseFromArray(buffer.ReadAddr(), buffer.ReadableSize());
                LOGSVR_TRACE("CachedMsgRes " << wTmpSeq << " resend size " << iLen << " pkg:" << pkg.DebugString());
                int iRet = NFPacketSendUtil::SendToClientFinally(GetClientSessionFast(), buffer.ReadAddr(), buffer.ReadableSize());
                CHECK_RET(iRet, "SendToClient error");
            }
        }
    }

    return 0;
}

int NFPlayer::MakePlayerDataCS(proto_ff::Proto_CSGetRoleRsp* pCSGetRoleRsp)
{
    CHECK_NULL(m_ullRoleId, pCSGetRoleRsp);
    NFPlayerBase::MakePlayerDataCS(pCSGetRoleRsp);
    for (int i = 0; i < (int)m_pPart.size(); ++i)
    {
        if (m_pPart[i])
        {
            m_pPart[i]->MakePlayerDataCS(pCSGetRoleRsp);
        }
    }
    return 0;
}

int NFPlayer::MakeCityPlayerData(proto_ff::CityRoleInfo* pGCCityRoleInfo)
{
    CHECK_NULL(m_ullRoleId, pGCCityRoleInfo);
    NFPlayerBase::MakeCityPlayerData(pGCCityRoleInfo);
    pGCCityRoleInfo->set_in_battle(IsInBattle());
    for (int i = 0; i < (int)m_pPart.size(); ++i)
    {
        if (m_pPart[i])
        {
            m_pPart[i]->MakeCityPlayerData(pGCCityRoleInfo);
        }
    }
    return 0;
}

int NFPlayer::SendSyncData()
{
    uint32_t nowTime = NF_ADJUST_TIMENOW();
    if (m_lastOnlineSyncTime + PLAYER_ONLINESYN_TIME_GAP <= (int)nowTime)
    {
        m_lastOnlineSyncTime = nowTime;
        NFOnlineService::SendSSSyncOnlineReq(this);
    }
    return 0;
}

int NFPlayer::SendMsgToClient(uint32_t nMsgId, const google::protobuf::Message& xData)
{
    return 0;
}

int NFPlayer::SendMsgToSnsServer(uint32_t nMsgId, const google::protobuf::Message& xData)
{
    return FindModule<NFIServerMessageModule>()->SendMsgToSnsServer(NF_ST_LOGIC_SERVER, GetLoginZoneId(), nMsgId, xData, m_ullRoleId);
}

int NFPlayer::SendMsgToWorldServer(uint32_t nMsgId, const google::protobuf::Message& xData)
{
    return FindModule<NFIServerMessageModule>()->SendMsgToWorldServer(NF_ST_LOGIC_SERVER, nMsgId, xData, m_ullRoleId);
}

int NFPlayer::SendMsgToGameServer(uint32_t nMsgId, const google::protobuf::Message& xData)
{
    return FindModule<NFIServerMessageModule>()->SendMsgToGameServer(NF_ST_LOGIC_SERVER, GetLoginZoneId(), nMsgId, xData, m_ullRoleId);
}

int NFPlayer::SendMsgToCityServer(uint32_t nMsgId, const google::protobuf::Message& xData)
{
    return FindModule<NFIServerMessageModule>()->SendMsgToCityServer(NF_ST_LOGIC_SERVER, GetLoginZoneId(), nMsgId, xData, m_ullRoleId);
}

int NFPlayer::SendTransToCityServer(uint32_t nMsgId, const google::protobuf::Message& xData, int transReq, int transRsp)
{
    return FindModule<NFIServerMessageModule>()->SendTransToCityServer(NF_ST_LOGIC_SERVER, GetLoginZoneId(), nMsgId, xData, m_ullRoleId, transReq, transRsp);
}

int NFPlayer::SendMsgToOnlineServer(uint32_t nMsgId, const google::protobuf::Message& xData)
{
    return FindModule<NFIServerMessageModule>()->SendMsgToOnlineServer(NF_ST_LOGIC_SERVER, GetLoginZoneId(), nMsgId, xData, m_ullRoleId);
}

int NFPlayer::SendTransToOnlineServer(uint32_t nMsgId, const google::protobuf::Message& xData, int transReq, int transRsp)
{
    return FindModule<NFIServerMessageModule>()->SendMsgToOnlineServer(NF_ST_LOGIC_SERVER, GetLoginZoneId(), nMsgId, xData, m_ullRoleId, transReq, transRsp);
}
